/*
	日志输出格式化类的实现
	- %d 时间
	- %p 日志等级
	- %c 日志器名称
	- %t 线程id
	- %f 源文件名
	- %l 源文件行号
	- %m 日志主体消息
	- %T 缩进
	- %n 换行
*/
#include <cassert>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <memory>
#include <sstream>
#include <unordered_set>
#include <vector>
#include <string_view>

#include "LogMessage.hpp"


namespace mylog {
    // 格式化的父类
    class FormatItem {
    public:
        using ptr = std::shared_ptr<FormatItem>;
        //向流中格式化输出指定内容
        virtual void Format(std::ostream& out, const LogMessage& msg) = 0;
    };


    // 日期格式化子项
    class TimeFormatItem : public FormatItem {
    public:
        // 日期格式化子项比较特殊，我们在使用时还需要指定时分秒的格式
        TimeFormatItem(const std::string& fmt = "%H:%M:%S") : _time_fmt(fmt.size() == 0 ? "%H:%M:%S" : fmt) {}
        // 重写父类的格式化接口
        void Format(std::ostream& out, const LogMessage& msg) override;

    private:
        std::string _time_fmt;
    };


    // 日志等级格式化子项
    class LevelFormatItem : public FormatItem {
    public:
        void Format(std::ostream& out, const LogMessage& msg) override {out << LogLevel::LevelToString(msg.m_level);}
    };

    // 日志器名称格式化子项
    class LoggerFormatItem : public FormatItem {
    public:
        void Format(std::ostream& out, const LogMessage& msg) override {out << msg.m_logger;}
    };

    // 线程id格式化子项
    class ThreadFormatItem : public FormatItem {
    public:
        void Format(std::ostream& out, const LogMessage& msg) override {out << msg.m_tid;}
    };

    // 文件名称格式化子项
    class FileFormatItem : public FormatItem {
    public:
        void Format(std::ostream& out, const LogMessage& msg) override {out << msg.m_file; }
    };

    // 文件行号格式化子项
    class LineFormatItem : public FormatItem {
    public:
        void Format(std::ostream& out, const LogMessage& msg) override {out << msg.m_line;}
    };

    // 日志有效信息格式化子项
    class MsgFormatItem : public FormatItem {
    public:
        void Format(std::ostream& out, const LogMessage& msg) override {out << msg.m_payload;}
    };

    // 制表符格式化子项
    class TabFormatItem : public FormatItem {
    public:
        void Format(std::ostream& out, const LogMessage& msg) override {out << "\t";}
    };

    // 新行格式化子项
    class NLineFormatItem : public FormatItem {
    public:
        void Format(std::ostream& out, const LogMessage& msg) override {out << "\n";}
    };

    // 原始字符格式化子项
    // 由于原始字符是我们想要在日志中添加的字符，在LogMessage中不存在对应的字段，所以我们需要传递参数
    class OtherFormatItem : public FormatItem {
    public:
        // 想要在日志中添加的字符
        OtherFormatItem(const std::string& str) : _str(str) {}
        void Format(std::ostream& out, const LogMessage& msg) override {out << _str;}

    private:
        std::string _str;
    };

    // 格式化器
    class Formatter {
    public:
        using ptr = std::shared_ptr<Formatter>;

        Formatter(const std::string& pattern = "[%d{%H:%M:%S}][%p][%c][%t][%f:%l]%T%m%n") : _pattern(pattern) {
            if (!ParsePattern()) throw std::runtime_error("Failed to parse pattern");
        }
        // 将日志输出到指定的流中
        void Format(std::ostream& out, const LogMessage& msg);
        // 将日志以返回值的形式进行返回
        std::string Format(const LogMessage& msg);
    private:
        // 解析格式化字符串并填充items数组
        bool ParsePattern();
        // 根据不同的格式化字符创建不同的格式化子类，是ParsePattern的子函数
        FormatItem::ptr CreateItem(const std::string& key, const std::string& val);
    private:
        // 格式化规则字符串
        std::string _pattern;
        // 按顺序存储指定的格式化对象
        std::vector<FormatItem::ptr> _items;
    };

} // namespace mylog
